#ifndef OPTFRAME_BASICILSPRVP_HPP_
#define OPTFRAME_BASICILSPRVP_HPP_

#include <math.h>
#include <vector>
#include <algorithm>

#include "../../OptFrame/Heuristic/IteratedLocalSearch.hpp"
#include "../../OptFrame/Heuristic/BasicILSPerturbation.hpp"

typedef int BasicHistory;

class BasicIteratedLocalSearchPRVP: public IteratedLocalSearch<BasicHistory, RepPRVP, MemPRVP>
{
protected:
	Heuristic<RepPRVP, MemPRVP>& h;
	Heuristic<RepPRVP, MemPRVP>& h_best;
	BasicILSPerturbation<RepPRVP, MemPRVP>& p;
	int iterMax, bestIter, maxIter;
	string filename;

	PRVPProblemInstance& pPRVP;

public:

	BasicIteratedLocalSearchPRVP(Evaluator<RepPRVP, MemPRVP>& e, PRVPProblemInstance& _pPRVP, Heuristic<RepPRVP, MemPRVP>& _h, Heuristic<RepPRVP, MemPRVP>& _h_best, BasicILSPerturbation<RepPRVP, MemPRVP>& _p, int _iterMax, string _filename) :
		IteratedLocalSearch<BasicHistory, RepPRVP, MemPRVP> (e), pPRVP(_pPRVP), h(_h), h_best(_h_best), p(_p), iterMax(_iterMax), filename(_filename)
	{
		bestIter = maxIter = 0;
	}

	virtual ~BasicIteratedLocalSearchPRVP()
	{
	}

	virtual BasicHistory& initializeHistory()
	{
		int& iter = *new int;
		iter = 0;

		return iter;
	}

	virtual void localSearch(SolutionPRVP& s, EvaluationPRVP& e, double timelimit, double target_f)
	{
#ifdef DEBUG
		if (!pPRVP.chech_solution(s))
		{
			cout << "Check antes: " << pPRVP.chech_solution(s) << endl;
			s.print();
			getchar();
		}
#endif
		h.exec(s, e, timelimit, target_f);
#ifdef DEBUG
		if (!pPRVP.chech_solution(s))
		{

			cout << "Check dps: " << pPRVP.chech_solution(s) << endl;
			s.print();
			getchar();
		}
#endif
	}

	virtual void perturbation(SolutionPRVP& s, EvaluationPRVP& e, double timelimit, double target_f, BasicHistory& history)
	{
		int iter = history;

		p.perturb(s, e, timelimit, target_f);

		// Incrementa a iteracao
		iter++;

		if (iter > maxIter)
			maxIter = iter;

		// Atualiza o historico
		history = iter;
	}

	virtual SolutionPRVP& acceptanceCriterion(const SolutionPRVP& s1, const SolutionPRVP& s2, BasicHistory& history)
	{
		if (IteratedLocalSearch<BasicHistory, RepPRVP, MemPRVP>::evaluator.betterThan(s2, s1))
		{
			// =======================
			//   Melhor solucao: 's2'
			// =======================
			EvaluationPRVP& e = IteratedLocalSearch<BasicHistory, RepPRVP, MemPRVP>::evaluator.evaluate(s2);
			cout << "Best fo: " << e.evaluation();
			cout << " on [iter " << history << "]" << endl;
			if (history > bestIter)
				bestIter = history;
			delete &e;

			// =======================
			//  Atualiza o historico
			// =======================
			// iter = 0
			history = 0;

			// =======================
			//    Retorna s2
			// =======================
			return s2.clone();
		}
		else
			return s1.clone();
	}

	virtual bool terminationCondition(BasicHistory& history)
	{
		int iter = history;

		/*if (iter < (iterMax * 0.8))
		 {

		 }
		 else
		 {

		 }*/

		return (iter >= iterMax);
	}

	void exec(SolutionPRVP& s, EvaluationPRVP& e, double timelimit, double target_f, LPool& lista)
	{

		maxIter = bestIter = 0;
		cout << "ILS exec(" << iterMax << ", " << target_f << "," << timelimit << ")" << endl;
		Timer tnow;

		BasicHistory* history = &initializeHistory();

		// 's0' <- GenerateSolution
		// 's*' <- localSearch 's'


		localSearch(s, e, (timelimit - (tnow.now())), target_f);

		SolutionPRVP* sStar = &s.clone();
		EvaluationPRVP* eStar = &e.clone();

		cout << "ILS starts: ";
		e.print();

		do
		{
			SolutionPRVP* s1 = &sStar->clone();
			EvaluationPRVP* e1 = &eStar->clone();
			perturbation(*s1, *e1, (timelimit - (tnow.now())), target_f, *history);
			localSearch(*s1, *e1, (timelimit - (tnow.now())), target_f);
#ifdef EXATO
			//adicionando a lista de rotoas
			lista.add(*e1, s1->getR());
#endif
			/*if(eStar->evaluation() == e1->evaluation())
			 {
			 cout<<"### CICLOU!!!!"<<endl;
			 }*/
			//SolutionPRVP* s2 = s1;
			//EvaluationPRVP* e2 = e1;

			SolutionPRVP* sStar1 = &acceptanceCriterion(*sStar, *s1, *history);

			delete sStar;
			delete eStar;
			delete s1;
			delete e1;

			sStar = sStar1;
			eStar = &evaluator.evaluate(*sStar);

		} while (evaluator.betterThan(target_f, eStar->evaluation()) && !terminationCondition(*history) && ((tnow.now()) < timelimit));

		SolutionPRVP* s2 = &sStar->clone();
		EvaluationPRVP* e2 = &eStar->clone();
		/*h_best.exec(*s2, *e2, timelimit, target_f);

		 if (e2->evaluation() < eStar->evaluation())
		 {
		 cout << "Best fo: " << e2->evaluation();
		 cout << " on BestImprovement" << endl;
		 sStar = s2;
		 eStar = e2;
		 }*/

		//gravando no arquivo
		/*FILE* file = fopen(filename.c_str(), "a");
		 if (!file)
		 {
		 cout << "Error creating file '" << filename << "'" << endl;
		 return;
		 }

		 fprintf(file, "Maior Iteracao Melhora: %d \t Maior Iteracao sem Melhora: %d \n", bestIter, maxIter);
		 fclose(file);*/

		e = *eStar;
		s = *sStar;

		//validaSol(s, e);

		delete eStar;
		delete sStar;
		delete s2;
		delete e2;
		delete history;

	}

	void validaSol(SolutionPRVP& s, EvaluationPRVP& e)
	{
		vector < vector<int> > &local = e.getM().getLocal_cli();

		for (int c = 1; c < pPRVP.nCli + 1; c++)
		{
			//cout<<"&&&&&&&&&&&&&&&& cliente: "<<c<<endl;
			bool encontrou = false;
			for (int escolha = 0; escolha < pPRVP.dados->at(c).at(NCOMB); escolha++)
			{

				vector<int> esc = pPRVP.decompoeEscolha(pPRVP.dados->at(c).at(NCOMB + escolha + 1), pPRVP.nPeriod);
				reverse(esc.begin(), esc.end());
				//cout<<"## esc: "<<esc<<endl;
				//cout<<"## atual: "<<local.at(c)<<endl;
				if (esc == local.at(c))
					encontrou = true;
			}
			if (!encontrou)
			{
				cout << "não está no padrão, cliente: " << c << endl;
				cout << "local: " << local.at(c) << endl;
				cout << "rep: " << s.getR() << endl;
				exit(1);
			}
		}

	}

};

#endif /*OPTFRAME_BASICILSPRVP_HPP_*/
